﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Interop;

namespace AutoCADDotNetLibrary
{
    /// <summary>
    /// 关键字参数
    /// </summary>
    public class KeywordParameter
    {
        /// <summary>
        /// 关键字参数
        /// </summary>
        public KeywordCollection Keywords { get; } = new KeywordCollection();
        /// <summary>
        /// 提示后，得到的关键字结果值
        /// </summary>
        public string StringResult { get; set; }
    }

    /// <summary>
    /// 编辑扩展
    /// </summary>
    public static class EditorExtention
    {
        /// <summary>
        /// 得到用户选择的类型（int,double,string,entity,point3d)（需要检查返回值是否为空）
        /// </summary>
        /// <typeparam name="T">int,double,string,entity,point3d</typeparam>
        /// <param name="ed">编辑</param>
        /// <param name="message">提示信息</param>
        /// <param name="defaultValue">默认值（int,double,string,Type[],point3d）</param>
        /// <param name="keywords">关键字</param>
        /// <returns>类型值，为null时，为取消</returns>
        public static object Get<T>(this Editor ed, string message, object defaultValue = null, KeywordParameter keywords = null)
        {
            message = "\n" + message.Trim();
            Action<KeywordCollection> act = keyword =>
            {
                keywords.Keywords.Cast<Keyword>().ToList().ForEach(x => keyword.Add(x.GlobalName, x.LocalName, x.DisplayName, x.Visible, x.Enabled));
                keyword.Default = keywords.Keywords.Default;
            };

            if (typeof(T) == typeof(int))
            {
                PromptIntegerOptions po = new PromptIntegerOptions(message);
                if (defaultValue != null)
                {
                    po.DefaultValue = (int)defaultValue;
                }
                if (keywords != null)
                {
                    act(po.Keywords);
                }

                PromptIntegerResult pr = ed.GetInteger(po);
                if (pr.Status == PromptStatus.Keyword)
                {
                    keywords.StringResult = pr.StringResult;
                    return pr.Value;
                }
                if (pr.Status == PromptStatus.OK)
                {
                    return pr.Value;
                }
                return null;
            }
            if (typeof(T) == typeof(double))
            {
                PromptDoubleOptions po = new PromptDoubleOptions(message);
                if (defaultValue != null)
                {
                    po.DefaultValue = (double)defaultValue;
                }
                if (keywords != null)
                {
                    act(po.Keywords);
                }

                PromptDoubleResult pr = ed.GetDouble(po);
                if (pr.Status == PromptStatus.Keyword)
                {
                    keywords.StringResult = pr.StringResult;
                    return pr.Value;
                }
                if (pr.Status == PromptStatus.OK)
                {
                    return pr.Value;
                }
                return null;
            }
            if (typeof(T) == typeof(string))
            {
                PromptStringOptions po = new PromptStringOptions(message);
                if (defaultValue != null)
                {
                    po.DefaultValue = (string)defaultValue;
                    po.AllowSpaces = true;
                    po.UseDefaultValue = true;
                }
                if (keywords != null)
                {
                    act(po.Keywords);
                }

                PromptResult pr = ed.GetString(po);
                if (pr.Status == PromptStatus.Keyword)
                {
                    keywords.StringResult = pr.StringResult;
                    return pr.StringResult;
                }
                if (pr.Status == PromptStatus.OK)
                {
                    return pr.StringResult;
                }
                return null;
            }
            if (typeof(T) == typeof(Entity))
            {
                PromptEntityOptions po = new PromptEntityOptions(message);
                if (defaultValue != null)
                {
                    po.SetRejectMessage("选择的实体类型不正确");

                    Type[] ts = (Type[])defaultValue;
                    foreach (Type item in ts)
                    {
                        po.AddAllowedClass(item, false);
                    }
                }
                if (keywords != null)
                {
                    act(po.Keywords);
                }

                PromptEntityResult pr = ed.GetEntity(po);

                if (pr.Status == PromptStatus.Keyword)
                {
                    keywords.StringResult = pr.StringResult;

                    Transaction tran = ed.Document.Database.TransactionManager.TopTransaction;
                    return tran.GetObject(pr.ObjectId, OpenMode.ForWrite);
                }
                if (pr.Status == PromptStatus.OK)
                {
                    Transaction tran = ed.Document.Database.TransactionManager.TopTransaction;
                    return tran.GetObject(pr.ObjectId, OpenMode.ForWrite);
                }
                return null;
            }
            if (typeof(T) == typeof(Point3d))
            {
                PromptPointOptions po = new PromptPointOptions(message);
                if (defaultValue != null)
                {
                    po.UseBasePoint = true;
                    po.BasePoint = (Point3d)defaultValue;
                }
                if (keywords != null)
                {
                    act(po.Keywords);
                }

                PromptPointResult pr = ed.GetPoint(po);
                if (pr.Status == PromptStatus.Keyword)
                {
                    keywords.StringResult = pr.StringResult;
                    return pr.Value;
                }
                if (pr.Status == PromptStatus.OK)
                {
                    return pr.Value;
                }
                return null;
            }

            throw new Exception("错误原因：不能选择" + nameof(T));
        }

        /// <summary>
        /// 得到选择的实体
        /// </summary>
        /// <param name="psr">选择对象的结果</param>
        /// <param name="mode">打开模式</param>
        /// <param name="forceOpenOnLockedLayer">锁图层数据是否可以写入数据</param>
        /// <returns></returns>
        public static IEnumerable<Entity> GetEntityByOK(this PromptSelectionResult psr, OpenMode mode, bool forceOpenOnLockedLayer = true)
        {
            if (psr.Status != PromptStatus.OK)
                return Enumerable.Empty<Entity>();
            if (psr.Value.Count == 0)
                return Enumerable.Empty<Entity>();

            Transaction trans = psr.Value[0].ObjectId.Database.TransactionManager.TopTransaction;
            if (forceOpenOnLockedLayer)
            {
                return psr.Value.GetObjectIds().Select(x => trans.GetObject(x, mode, false, forceOpenOnLockedLayer)).Cast<Entity>();
            }
            else
            {
                return psr.Value.GetObjectIds().Select(x =>
                {
                    try
                    {
                        return trans.GetObject(x, mode, false, forceOpenOnLockedLayer);
                    }
                    catch (Autodesk.AutoCAD.Runtime.Exception ex) when (ex.ErrorStatus == Autodesk.AutoCAD.Runtime.ErrorStatus.OnLockedLayer)
                    {
                        return null;
                    }
                }).Where(x => x != null).Cast<Entity>();
            }
        }

        /// <summary>
        /// 得到框选的点集
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="messagePoint1">选择的第一个点</param>
        /// <param name="messagePoint2">选择的第二个点</param>
        /// <returns>点集</returns>
        public static Point3dCollection GetSquareSelect(this Editor ed, string messagePoint1, string messagePoint2)
        {
            Point3d? pt1 = ed.Get<Point3d>(messagePoint1) as Point3d?;
            if (pt1 == null)
            {
                return default;
            }

            PromptPointResult pt2 = ed.GetCorner(messagePoint2, pt1.Value);
            if (pt2 == null)
            {
                return default;
            }

            Point3dCollection pts = new Point3dCollection()
            {
                pt1.Value,
                new Point3d(pt1.Value.X,pt2.Value.Y,0),
                pt2.Value,
                new Point3d(pt2.Value.X,pt1.Value.Y,0),
            };

            return pts;
        }

        /// <summary>
        /// 同步：显示视图
        /// </summary>
        /// <param name="ed"></param>
        /// <param name="pts"></param>
        public static void SetZoomWSynchronize(this Editor ed, IEnumerable<Point3d> pts)
        {
            ResultBuffer rb = new ResultBuffer
            {
                new TypedValue(5005, "zoom"),
                new TypedValue(5005, "w"),
                new TypedValue(5009, new Point3d(pts.Cast<Point3d>().Min(x => x.X), pts.Cast<Point3d>().Min(x => x.Y), 0)),
                new TypedValue(5009, new Point3d(pts.Cast<Point3d>().Max(x => x.X), pts.Cast<Point3d>().Max(x => x.Y), 0))
            };
            ARX.AcedCmd(rb);
        }

        /// <summary>
        /// 同步：上一个视图
        /// </summary>
        /// <param name="ed"></param>
        public static void SetZoomPSynchronize(this Editor ed)
        {
            ResultBuffer rb1 = new ResultBuffer
            {
                new TypedValue(5005, "zoom"),
                new TypedValue(5005, "p")
            };
            ARX.AcedCmd(rb1);
        }

        /// <summary>
        /// 显示左下与右上的端点的范围
        /// </summary>
        /// <param name="ed">文档</param>
        /// <param name="LowerLeft">左下端点</param>
        /// <param name="UpperRight">右上端点</param>
        public static void Zoom(this Editor ed, Point3d LowerLeft, Point3d UpperRight)
        {
            ((AcadApplication)Application.AcadApplication).ZoomWindow(new double[] { LowerLeft.X, LowerLeft.Y, 0 }, new double[] { UpperRight.X, UpperRight.Y, 0 });
        }

        /// <summary>
        /// 显示包含所有点的范围
        /// </summary>
        /// <param name="ed">文档</param>
        /// <param name="pts">点集</param>
        public static void Zoom(this Editor ed, IEnumerable<Point3d> pts)
        {
            double minX = pts.Select(x => x.X).Min();
            double maxX = pts.Select(x => x.X).Max();
            double minY = pts.Select(x => x.Y).Min();
            double maxY = pts.Select(x => x.Y).Max();

            ((AcadApplication)Application.AcadApplication).ZoomWindow(new double[] { minX, minY, 0 }, new double[] { maxX, maxY, 0 });
        }

        /// <summary>
        /// 显示整个实体的范围
        /// </summary>
        /// <param name="ed">文档</param>
        /// <param name="ent">实体</param>
        public static void Zoom(this Editor ed, Entity ent)
        {
            ed.Zoom(ent.GeometricExtents.MinPoint, ent.GeometricExtents.MaxPoint);
        }

        /// <summary>
        /// 显示整个实体集的范围
        /// </summary>
        /// <param name="ed">文档</param>
        /// <param name="ents">实体集</param>
        public static void Zoom(this Editor ed, IEnumerable<Entity> ents)
        {
            ed.Zoom(ents.Select(x => x.GeometricExtents.MinPoint).Concat(ents.Select(x => x.GeometricExtents.MaxPoint)));
        }

        /// <summary>
        /// 所有图形的范围(双击鼠标中键的操作)
        /// </summary>
        /// <param name="ed">文档</param>
        public static void ZoomALL(this Editor ed)
        {
            ((AcadApplication)Application.AcadApplication).ZoomExtents();
        }

        /// <summary>
        /// 设置选择集(指选中状态)(只是一种思路)(请在<see cref="Autodesk.AutoCAD.Runtime.CommandFlags.UsePickSet"/>不使用的情况使用原生api，<see cref="Editor.SetImpliedSelection(ObjectId[])"/>)
        /// </summary>
        /// <param name="ed">文档</param>
        /// <param name="ids">实体集合</param>
        /// <example>例子：(setq ents (ssadd))(ssadd (handent "45E") ents)(ssadd (handent "462") ents)(sssetfirst nil ents)</example> 
        [Obsolete("请使用原生Editor.SetImpliedSelection(ObjectId[])")]
        public static void SetImpliedSelection2006(this Editor ed, params ObjectId[] ids)
        {
            //生成lsp文件
            string s = "defun SetfirstSelectionSet ()(setq ents (ssadd))" + string.Join("", ids.Select(x => "(ssadd (handent \"" + x.Handle + "\") ents)").ToArray())
                     + "(sssetfirst nil ents)(setvar \"cmdecho\" 0)";

            RunlspFunction(ed.Document, s);
        }

        /// <summary>
        /// 运行lsp脚本（c#命令之后运行lsp脚本）
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="lspContent">lsp内容</param>
        public static void RunlspFunction(this Document doc, string lspContent)
        {
            string dir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            //确定文件名
            string lspFile = dir + $"\\LspFunctionTempFile@@@.lsp";
            File.WriteAllText(lspFile, lspContent);

            //使用lsp文件
            doc.LoadLsp(lspFile);

            //删除lsp文件
            Thread thread = new Thread(() =>
            {
                Thread.Sleep(5000);
                File.Delete(lspFile);
            });
            thread.IsBackground = true;
            thread.Start();
        }
    }
}